#coding=utf-8
#导入蓝图对象
from . import api
from ihome import redis_store,constants,db
from flask import current_app,jsonify,json,g,request,session
from ihome.models import Area,House,Facility,HouseImage,User,Order
from ihome.utils.response_code import RET
from ihome.utils.commons import login_required
from ihome.utils.image_storage import storage
import datetime


@api.route('/areas',methods=['GET'])
def get_areas_info():
    """
    获取城区信息:
    缓存----磁盘----缓存
    1/尝试从redis中获取城区信息
    2/判断查询结果是否有数据,如果有数据
    3/留下访问redis中城区信息的记录,在日志中
    4/需要查询mysql数据库
    5/判断查询结果，如果没有返回错误信息，
    6/如果有，定义一个容器，存储查询结果
    7/遍历查询结果，添加到列表中
    8/对查询结果进行序列化，转成json
    9/存到redis缓存中，
    10/返回结果
    """
    # 1 / 尝试从redis中获取城区信息
    try:
        areas=redis_store.get('area_info')
    except Exception as e:
        current_app.logger.error(e)
        areas=None
    # 2 / 判断查询结果是否有数据, 如果有数据
    if areas:
        # 3 / 留下访问redis中城区信息的记录, 在日志中
        current_app.logger.info('hit redis areas info')
        return '{"errno":0,"errmsg":"OK","data":%s}'%areas

    # 4 / 需要查询mysql数据库
    try:
        areas = Area.query.all()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg='查询城区信息异常')
    # 5 / 判断查询结果，如果没有返回错误信息，
    if not areas:
        return jsonify(errno=RET.NODATA,errmsg='无城区信息')
    # 6 / 如果有，定义一个容器，存储查询结果

    areas_list=[]
    # 7 / 遍历查询结果，添加到列表中
    for area in areas:
        areas_list.append(area.to_dict())
    # # 8 / 对查询结果转成json
    print areas_list
    areas_json=json.dumps(areas_list)

    # 9 / 存到redis缓存中，
    try:
        redis_store.setex('area_info',constants.AREA_INFO_REDIS_EXPIRES,areas_json)
    except Exception as e:
        current_app.logger.error(e)
    # 10 / 返回结果
    resp= '{"errno":0,"errmsg":"OK","data":%s}'%areas_json
    return resp


@api.route('/houses',methods=['POST'])
@login_required
def save_house_info():
    """
    发布新房源:
    1/确认用户身份id
    2/获取参数，get_json()接口文档中提到的参数
    3/判断数据的存在，
    4/获取详细的参数信息 仅指房屋的基本的参数 不含配套设施
    5/检查参数的完整性
    6/对价格和押金参数进行转换，由元转为分
    7/构造模型类对象，准备存储数据
    8/判断配套设施的存在，
    9/如果存在，需要对配套设施进行过滤查询，后端只会保存数据库中已经定义的配套设施信息
    facilties =Facility.query.filter(Facility.id.in_(facility)).all()
    house.facilities  = facilities 存到了第三个表中
    10/保存数据到数据库中
    11/不用放到redis缓存中
    12/返回结果house_id ，让后面上传房屋图片和房屋进行关联

    """

    #确认用户身份
    user_id = g.user_id
    #获取参数post
    house_data = request.get_json()
    #检查参数的存在
    if not house_data:
        return jsonify(errno=RET.PARAMERR,errmsg='参数错误')
    #获取详细的房屋参数信息--基本信息，除配套设施之外
    title=house_data.get('title')
    area_id = house_data.get('area_id')  # 房屋城区
    address = house_data.get('address')  # 详细地址
    price = house_data.get('price')  # 房屋价格
    room_count = house_data.get('room_count')  # 房屋数目
    acreage = house_data.get('acreage')  # 房屋面积
    unit = house_data.get('unit')  # 房屋户型
    capacity = house_data.get('capacity')  # 人数上限
    beds = house_data.get('beds')  # 卧床配置
    deposit = house_data.get('deposit')  # 押金
    min_days = house_data.get('min_days')  # 最小入住天数
    max_days = house_data.get('max_days')  # 最大入住天数

    #检查参数的完整性--如果不完整，参数缺失
    if not all([title,area_id,address,price,room_count,acreage,unit,capacity,beds,deposit,min_days,max_days]):
        return jsonify(errno=RET.PARAMERR,errmsg='参数缺失')
    #对价格参数进行转换，由元转分 需要try
    try:
        price=int(float(price)*100)
        deposit=int(float(deposit)*100)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR,errmsg='价格数据错误')
    #构造模型类对象，准备存储数据
    house = House()
    house.user_id = user_id
    house.area_id = area_id
    house.title = title
    house.address = address
    house.price = price
    house.room_count = room_count
    house.acreage = acreage
    house.unit = unit
    house.capacity = capacity
    house.beds = beds
    house.deposit = deposit
    house.min_days = min_days
    house.max_days = max_days
    #尝试获取房屋配套设施参数
    facility = house_data.get('facility')
    #判断配套设施存在
    if facility:
        #查询数据库，对房屋配套设施进行过滤查询，确保配套设备信息在数据库中存在
        try:
            facilities=Facility.query.filter(Facility.id.in_(facility)).all()
            house.facilities = facilities
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(errno=RET.DBERR,errmsg='查询配套设施异常')
    #保存房屋配套设施信息，配套设施存储的数据，存在第三张表中，
    #关系引用在数据库中没有具体的字段
    try:
        db.session.add(house)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR,errmsg='保存房屋数据失败')

    #返回结果 house_id 是用来后面实现上传房屋图片做准备
    return jsonify(errno=RET.OK,errmsg='OK',data={'house_id':house.id})

@api.route('/houses/<int:house_id>/images',methods=['POST'])
@login_required
def save_house_image(house_id):
    """
    上传房屋图片
    1/获取参数，request.files属性
    2/判断获取结果
    3/根据house_id查询数据库，House模型类
    4/判断查询结果，确认房屋的存在
    5/读取图片数据
    6/调用七牛云接口，上传图片
    7/保存图片名称
    8/构造HouseImage模型类对象，准备存储房屋图片数据
    9/判断房屋默认图片是否设置，如未设置，默认添加当前图片为主图片;
    10/保存房屋对象数据
    11/提交数据到数据库中
    12/拼接图片的绝对路径
    13/返回结果
    """
    #获取图片文件
    house_image = request.files.get('house_image')
    #校验参数的存在
    if not house_image:
        return jsonify(errno=RET.NODATA,errmsg='未上传房屋图片')
    #确认房屋的存在
    try:
        house=House.query.filter_by(id=house_id).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg='查询房屋数据错误')
    #判断查询结果
    if not house:
        return jsonify(errno=RET.NODATA,errmsg='房屋不存在')
    #读取图片数据
    house_image_data=house_image.read()
    #调用七牛云接口，上传房屋图片，
    try:
        image_name=storage(house_image_data)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.THIRDERR,errmsg='上传图片失败')
    #保存房屋图片数据，构造模型类对象
    house_image = HouseImage()
    house_image.house_id= house_id
    house_image.url = image_name
    #添加数据到数据库会话对象
    db.session.add(house_image)
    #判断房屋主图片是否设置，如果未设置，默认添加当前图片为主图片
    if not house.index_image_url:
        house.index_image_url= image_name
        db.session.add(house)
    #提交数据到mysql数据库中
    try:
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg='保存房屋图片失败')
    #拼接图片的绝对路径
    image_url = constants.QINIU_DOMIN_PREFIX + image_name
    #返回结果
    return jsonify(errno=RET.OK,errmsg='OK',data={'url':image_url})

@api.route('/user/houses',methods=['GET'])
@login_required
def get_user_houses():
    """
    我的房源
    1/确认用户身份
    2/根据用户id查询数据库
    3/使用关系定义，返回的对象，实现一对多的查询
    4/遍历查询结果，调用模型类中方法
    5/如果有返回数据
    :return:
    """
    #获取用户Id
    user_id = g.user_id
    #查询数据库
    #确认用户存在
    try:
        user = User.query.get(user_id)
        #用户存在，使用反向引用，实现一对多的查询，获取该用户的所有房屋信息
        houses = user.houses
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg='查询longue房屋数据失败')
    #如果房屋数据存在，遍历查询结果，添加到列表中
    house_list = []
    if houses:
        for house in houses:
            house_list.append(house.to_basic_dict())
    #返回结果
    return jsonify(errno=RET.OK,errmsg='OK',data={'houses':house_list})

@api.route('/houses/index',methods=['GET'])
def get_houses_inde():
    """项目首页房屋图片的幻灯片
          缓存--磁盘---缓存
    """
    #尝试从redis缓存中获取首页幻灯片信息
    try:
        ret=redis_store.get('home_page_data')
    except Exception as e:
        current_app.logger.error(e)
        ret=None
    #判断获取结果
    if ret:
        #留下访问redis数据库的记录
        current_app.logger.info('hit redis houses index info')
        return '{"errno":0,"errmsg":"OK","data":%s}'%ret
    #如未获取，需要查询mysql数据库
    try:
        houses=House.query.order_by(House.order_count.desc()).limit(constants.HOME_PAGE_MAX_HOUSES)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg='查询房屋数据失败')
    if not houses:
        return jsonify(errno=RET.NODATA, errmsg='无房屋数据')
    #遍历获取到的符合添加的房屋数据，进一步筛选出设置类主图片的房屋数据
    houses_list = []
    for house in houses:
        #如果房屋未设置主图片，默认不添加
        if not house.index_image_url:
            continue
        houses_list.append(house.to_basic_dict())
    #redis中数据为json格式，所以需要把房屋数据转成Json
    houses_json = json.dumps(houses_list)
    #存入到redis缓存中
    try:
        redis_store.setex('home_page_data',constants.HOME_PAGE_DATA_REDIS_EXPIRES,houses_json)
    except Exception as e:
        current_app.logger.error(e)
    #构造响应报文，返回幻灯片信息
    resp = '{"errno"：0,"errmsg":"OK","data":%s}'%houses_json
    return resp

@api.route('/houses/<int:house_id>',methods=['GET'])
def get_house_detail(house_id):
    """获取房屋详情数据

        点击首页图片进入房屋详情页面，
        详情页面数据也放入缓存中
        缓存-磁盘（数据库）-缓存
    """
    #使用session对象获取用户身份
    user_id = session.get('user_id',-1)
    #判断house_id 存在
    if not house_id:
        return jsonify(errno=RET.PARAMERR,errmsg='参数错误')
    #尝试读取reids数据库，获取房屋详情数据
    try:
        ret =redis_store.get('house_info_%s'%house_id)
    except Exception as e:
        current_app.logger.error(e)
        ret = None
    #判断获取结果
    if ret:
        #留下访问redis的记录
        current_app.logger.info('hit redis house detail')
        return '{"errno":0,"errmsg":"OK","data":{"user_id":%s,"house":%s}}'%(user_id,ret)
    #查询磁盘数据库
    try:
        house = House.query.get(house_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg='查询房屋数据失败')
    #判断查询结果
    if not house:
        return jsonify(errno=RET.NODATA,errmsg='无房屋数据')
    #转成Json 数据
    try:
        house_data = house.to_full_dict()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg='查询房屋详情数据失败')
    house_json = json.dumps(house_data)
    #把房屋详情数据存入redis缓存中
    try:
        redis_store.setex('house_info_%s'%house_id,constants.HOME_PAGE_DATA_REDIS_EXPIRES,house_json)
    except Exception as e:
        current_app.logger.error(e)
    resp = '{"errno":0,"errmsg":"OK","data":{"user_id":%s,"house":%s}}'%(user_id,house_json)
    return resp

@api.route('/houses',methods=['GET'])
def get_houses_list():
    """
        获取房屋列表信息
        缓存----磁盘----缓存
        获取参数---检查参数---查询数据---返回结果
        1/获取参数:aid,sd,ed,sk,p
        2/需要对排序条件和页面两个参数,进行默认处理
        3/需要对日期参数进行判断,并且进行格式化
        4/需要对页数进行格式化
        5/尝试从redis缓存中获取房屋列表信息
        6/让一个键对应多条数据的存储,需要hash数据类型,构造hash对象的键
        redis_key = 'houses_%s_%s_%s_%s' %(aid,sd,ed,sk)
        ret = redis_store.hget(redis_key,page)
        7/如果有数据,留下访问的记录,直接返回
        8/查询mysql数据库
        9/首先定义容器存储查询数据库的过滤条件
        10/判断区域参数的存在,如果有把区域信息添加到列表中
        11/需要判断日期参数的存在,把用户选择的日期和订单表中的日期进行比较,找到日期不冲突的房屋返回给客户端
        12/根据容器中存储的过滤条件,执行查询排序,price/crate_time
        houses = House.query.filter(过滤条件).order_by(House.order_count.desc())
        13/对排序结果进行分页
        houses_page = houses.paginate(page,每页条目数,False)
        total_page = houses_page.pages
        houses_list = houses_page.items
        14/定义容器,houses_dict_list遍历分页后的房屋数据,调用模型类中的to_basic_dict()方法
        15/构造响应报文
        resp = {"errno":0,"errmsg":"OK","data":{"houses":houses_dict_list,"total_page":total_page,"current_page":page}}
        16/序列化数据,准备存入缓存中
        resp_json = json.dumps(resp)
        17/判断用户选择的页数必须小于等于分页后的总页数,本质上用户选择的页数必须要有数据
        18/构造hash数据类型的键,为了确保数据的完整性以及有效期的一致性
        需要使用事务
        pip = redis_store.pipline()
        pip.multi()
        pip.hset(redis_key,page,resp_json)
        pip.expire(redis_key,7200)
        pip.execute()
        19/返回结果,return resp_json
        :return:
        """
    #获取参数，区域信息/开始日期/结束日期/排序条件/页数
    area_id = request.args.get('aid','')
    start_date_str = request.args.get('sd','')
    end_date_str = request.args.get('ed','')
    sort_key = request.args.get('sk','new')
    page = request.args.get('p','1')
    #检查日期参数
    try:
        #定义变量存储格式化后的日期
        start_date,end_date = None,None
        #如果用户选择了开始日期
        if start_date_str:
            start_date = datetime.datetime.strptime(start_date_str,'%Y-%m-%d')
        #如果用户选择了结束日期
        if end_date_str:
            end_date = datetime.datetime.strptime(end_date_str,'%Y-%m-%d')
        #如果用户两个日期都选择了，判断用户选择的日期必须至少是一天
        if start_date_str and end_date_str:
            #断言用户选择的日期必须至少是一天
            assert start_date <= end_date
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR,errmsg='日期格式错误')
    #对页数进行格式化
    try:
        page=int(page)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR,errmsg='页数格式错误')
    #尝试从redis 缓存中获取房屋列表信息
    #一个键对应多条数据，使用hash数据类型
    try:
        redis_key = 'houses_%s_%s_%s_%s'%(area_id,start_date_str,end_date_str,sort_key)
        ret = redis_store.hget(redis_key,page)
    except Exception as e:
        current_app.logger.error(e)
        ret = None
    #判断ret是否存在
    if ret:
        #留下访问redis数据的记录
        current_app.logger.info('hit redis houses list info')
        return ret
    # 查询mysql 数据库
    try:
        #定义容器，存储过滤条件
        params_filter= []
        #如果城区参数存在
        if area_id:
            #添加的是城区数据的对象
            params_filter.append(House.area_id==area_id)
            print('======')
            print(params_filter)
        #如果开始和结束参数都存在，目标是查询日期不冲突的房屋
        if start_date and end_date:
            #查询日期冲突的订单
            conflict_orders = Order.query.filter(Order.begin_date<=end_date,Order.end_date>=start_date).all()
            #查询有冲突的房屋
            conflict_houses_id = [order.house_id for order in conflict_orders]
            #判断有冲突的房屋是否存在，对有冲突的房屋进行取反，获取日期不冲突的房屋
            if conflict_houses_id:
                params_filter.append(House.id.notin_(conflict_houses_id))
        #如果用户只选择了一个开始日期
        elif start_date:
            conflict_orders = Order.query.filter(Order.end_date>=start_date).all()
            conflict_houses_id = [order.house_id for order in conflict_orders]
            if conflict_houses_id:
                params_filter.append(House.id.notin_(conflict_houses_id))
        #如果用户选择类一改结束日期
        elif end_date:
            conflict_orders = Order.query.filter(Order.begin_date<=end_date).all()
            conflict_houses_id = [order.house_id for order in conflict_orders]
            if conflict_houses_id:
                params_filter.append(House.id.notin_(conflict_houses_id))
        print('########')
        print(params_filter)
        print(type(params_filter[0]))
        #判序条件，按照房屋成交次数排序
        if 'booking' == sort_key:
            #*params_filter 是往查询方法进行拆包
            houses = House.query.filter(*params_filter).order_by(House.order_count.desc())
        #按照价格进行升序和降序排序
        elif 'price-inc' == sort_key:
            houses = House.query.filter(*params_filter).order_by(House.price.asc())
        elif 'price-des' == sort_key:
            houses = House.query.filter(*params_filter).order_by(House.price.desc())
        #默认按照房屋创建时间
        else:
            houses = House.query.filter(*params_filter).order_by(House.create_time.desc())
        #对排序后的房屋进行分页，page代表当前页，每页条目数，Flase分页异常不报错
        houses_page = houses.paginate(page,constants.HOUSE_LIST_PAGE_CAPACITY,False)
        print('0909090-')
        print(houses_page)
        #获取分页后的房屋数据
        houses_list = houses_page.items
        print '分页后的房屋数据',houses_list
        #获取分页后的总页数
        total_page = houses_page.pages
        print '总页数',total_page
        #定义容器，遍历分页后的房屋数据，将数据变为字典形式，需要调用模型类中的方法
        houses_dict_list = []
        for house in houses_list:
            houses_dict_list.append(house.to_basic_dict())
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg = '查询房屋列表信息失败')
    #构造响应数据
    resp = {"errno":0,"errmsg":"OK","data":{"houses":houses_dict_list,"total_page":total_page,"current_page":page}}
    #序列化数据，准备存入缓存中
    resp_json = json.dumps(resp)
    #判断用户请求的页数必须小于等于分页后的总页数,设置缓存，如果大于就不设置缓存
    if page <= total_page:
        redis_key = 'houses_%s_%s_%s_%s' % (area_id, start_date_str, end_date_str, sort_key)
        #多条数据的存储，为了确保数据的完整性和一致性，需要使用事务
        pip = redis_store.pipeline()
        try:
            #开启事务
            pip.multi()
            #保存数据
            pip.hset(redis_key,page,resp_json)
            #设置过期时间
            pip.expire(redis_key,constants.HOUSE_LIST_REDIS_EXPIRES)
            #执行事务
            pip.execute()
        except Exception as e:
            current_app.logger.error(e)
    #返回结果
    return resp_json








